// Copyright (c) Meta Platforms, Inc. and affiliates.

#ifndef ZSTRONG_COMPRESS_NAME_H
#define ZSTRONG_COMPRESS_NAME_H

#include "openzl/common/allocation.h"
#include "openzl/common/errors_internal.h"
#include "openzl/shared/portability.h"
#include "openzl/shared/string_view.h"

ZL_BEGIN_C_DECLS

#define ZL_PREFIX_MAX_LEN 100
#define ZL_NAME_MAX_LEN 127

typedef struct {
    /**
     * For anchors, this is the name the user passed in, excluding the leading
     * '!'. It is up to the caller to validate that the user didn't violate the
     * uniqueness property. This name has no '!' or '#' characters.
     *
     * Otherwise, this is a name that is generated by concatenating #{id} to the
     * prefix the user provided. This name has exactly one '#' character.
     */
    StringView unique;
    /**
     * For anchors, this is the same as unique.
     * Otherwise, this is the possibly not unique name the user provided.
     */
    StringView prefix;
    /**
     * Whether or not this name is an anchor name (user provided name started
     * with '!').
     */
    bool isAnchor;
} ZL_Name;

/**
 * Validate @p prefix and construct a unique name with the given @p prefix and
 * @p uniqueID.
 *
 * 1. Validates that @p prefix doesn't contain '#'.
 * 2. Validates that @p prefix doesn't contain '!' except in the first byte.
 * 3. Validates that @p prefix doesn't start with "!zl." (the standard prefix).
 * 4. If the name is not an anchor (starts with '!'), constructs the unique
 *    name "${prefix}#${uniqueID}".
 * 5. Transfers both the prefix & unique names into @p arena for stability.
 *
 * @param[out] name On success initializes @p name.
 *
 * @returns Success or an error
 *
 * @note This function does not guarantee uniqueness of anchors, that is up to
 * the caller.
 */
ZL_Report ZL_Name_init(
        ZL_Name* name,
        Arena* arena,
        const char* prefix,
        ZL_IDType uniqueID);

/**
 * Construct a ZL_Name from a standard name. This validates that the standard
 * name is an anchor that starts with "!zl.".
 */
ZL_Name ZS2_Name_wrapStandard(const char* name);

/**
 * Constructs a ZL_Name for the purpose of looking up @p key in a map keyed by
 * ZL_Name.
 */
ZL_INLINE ZL_Name ZL_Name_wrapKey(const char* key)
{
    ZL_ASSERT_NE(key[0], '!');
    const size_t len = strlen(key);
    ZL_Name name     = {
            .unique   = StringView_init(key, len),
            .prefix   = StringView_init(key, len),
            .isAnchor = false,
    };
    return name;
}

/// @returns True if the name object is empty (standard graphs / nodes don't
/// fill their ZL_Name)
ZL_INLINE bool ZL_Name_isEmpty(const ZL_Name* name)
{
    return name->unique.data == NULL;
}

/// @returns The unique name c-str
ZL_INLINE const char* ZL_Name_unique(const ZL_Name* name)
{
    return name->unique.data;
}

/// @returns The prefix name c-str (excluding '#${unique}' for non-anchors)
ZL_INLINE const char* ZL_Name_prefix(const ZL_Name* name)
{
    return name->prefix.data;
}

ZL_INLINE size_t ZL_Name_hash(const ZL_Name* name)
{
    return StringView_hash(&name->unique);
}

ZL_INLINE bool ZL_Name_eq(const ZL_Name* lhs, const ZL_Name* rhs)
{
    return StringView_eq(&lhs->unique, &rhs->unique);
}

ZL_END_C_DECLS

#endif
